package mac5789.hamcycle;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import mac5789.graph.StegerWormaldRandomGraph;
import mac5789.sat.SatProblem;
import mac5789.solver.SatSolver;
import mac5789.solver.SatSolverResult;

/**
 * Medidor de satisfatibilidade que executa um número fixo de vezes de
 * experimentos.
 * 
 * @author lundberg
 * 
 */
public class FixedRunsHamCycleMeasurer implements HamCycleMeasurer {

    private ExecutorService executor;

    private final int runs;

    private final Random random;

    private final SatSolver solver;

    private final HamCycleToSat converter;

    /**
     * Construtor
     * 
     * @param solver
     *            solver
     * @param seed
     *            seed
     * @param runs
     *            runs
     */
    public FixedRunsHamCycleMeasurer(SatSolver solver, HamCycleToSat converter, ExecutorService executor, long seed, int runs) {
        super();
        this.solver = solver;
        this.converter = converter;
        this.random = new Random(seed);
        this.runs = runs;
        this.executor = executor;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HamCycleMeasurement measure(final int vertexes, final int degree) {
        int satisfiables = 0;
        double time = 0;
        List<Future<SatSolverResult>> results = new ArrayList<Future<SatSolverResult>>(this.runs);
        for (int i = 0; i < this.runs; i++) {
            results.add(this.executor.submit(new Callable<SatSolverResult>() {
                @Override
                public SatSolverResult call() throws Exception {
                    return FixedRunsHamCycleMeasurer.this.solver.solve(FixedRunsHamCycleMeasurer.this.aRandomProblemWith(vertexes, degree));
                }
            }));
        }
        System.out.print("Measuring");
        for (Future<SatSolverResult> result : results) {
            try {
                SatSolverResult solverResult = result.get();
                System.out.print(".");
                if (solverResult.isSatisfiable()) {
                    satisfiables++;
                }
                time += solverResult.getCpuTime();
            } catch (Exception e) {
                this.executor.shutdownNow();
                throw new RuntimeException(e);
            }
        }
        System.out.println("");
        HamCycleMeasurement measurement = new HamCycleMeasurement();
        measurement.setSatisfiable((1.0 * satisfiables) / this.runs);
        measurement.setAverageTime(time / this.runs);
        return measurement;
    }

    private SatProblem aRandomProblemWith(int vertexes, int degree) {
        return this.converter.toSat(new StegerWormaldRandomGraph(vertexes, degree, this.random.nextLong()));
    }
}
